/*
 * Copyright (C) 2012  Romain DUBOIS
 * 
 * This file is part of NeMoS - Network and Modular Software
 * 
 * NeMoS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * NeMoS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with NeMoS.  If not, see <http://www.gnu.org/licenses/>.
 */
package nemos.util.osgi;

import java.util.HashMap;
import java.util.SortedMap;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

/**
 * ServiceHashMap : {@link HashMap}-based {@link IServiceMap} implementation.
 * 
 * @param <S>
 *            The services type
 * 
 * @author Romain DUBOIS
 */
public class ServiceHashMap<S> extends HashMap<String, ServiceReference<S>> implements
        IServiceMap<S> {

    /** Serial version */
    private static final long serialVersionUID = 1L;

    /** The services map */
    private SortedMap<ServiceReference<S>, S> mServices;
    /** The mapping property name */
    private final String mProperty;
    /** The bundle context */
    private final BundleContext mContext;

    /**
     * Constructor
     * 
     * @param pProperty
     *            The property used to map services
     */
    public ServiceHashMap(final String pProperty) {
        this(pProperty, null);
    }

    /**
     * Constructor
     * 
     * @param pProperty
     *            The property used to map services
     * @param pContext
     *            The bundle context
     */
    public ServiceHashMap(final String pProperty, final BundleContext pContext) {
        super();
        mProperty = pProperty;
        mContext = pContext;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.util.Map#put(java.lang.Object, java.lang.Object)
     */
    @Override
    public ServiceReference<S> put(final String pKey, final ServiceReference<S> pValue) {
        if (mContext == null) {
            mServices.put(pValue, null);
        } else {
            mServices.put(pValue, mContext.getService(pValue));
        }

        return add(pKey, pValue);
    }

    /**
     * Add a service reference to the main map if needed.
     * 
     * @param pKey
     *            The key to use
     * @param pReference
     *            The new service reference
     * @return The previous value
     */
    private ServiceReference<S> add(final String pKey, final ServiceReference<S> pReference) {
        final ServiceReference<S> lPrevious = get(pKey);
        if (lPrevious.compareTo(pReference) > 0) {
            super.put(pKey, pReference);
        }
        return lPrevious;
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.util.Map#remove(java.lang.Object)
     */
    @Override
    public ServiceReference<S> remove(@SuppressWarnings("unused") final Object pKey) {
        throw new UnsupportedOperationException();
    }

    /**
     * {@inheritDoc}
     * 
     * @see java.util.Map#clear()
     */
    @Override
    public void clear() {
        super.clear();

        if (mContext != null) {
            for (final ServiceReference<S> lReference : mServices.keySet()) {
                mContext.ungetService(lReference);
            }
        }
        mServices.clear();
    }

    /**
     * {@inheritDoc}
     * 
     * @see nemos.util.osgi.IServiceMap#getService(java.lang.String)
     */
    @Override
    public S getService(final String pKey) {
        return mServices.get(get(pKey));
    }

    /**
     * {@inheritDoc}
     * 
     * @see nemos.util.osgi.IServiceMap#add(org.osgi.framework.ServiceReference)
     */
    @Override
    public void add(final ServiceReference<S> pReference) {
        put((String) pReference.getProperty(mProperty), pReference);
    }

    /**
     * {@inheritDoc}
     * 
     * @see nemos.util.osgi.IServiceMap#add(org.osgi.framework.ServiceReference,
     *      java.lang.Object)
     */
    @Override
    public void add(final ServiceReference<S> pReference, final S pService) {
        mServices.put(pReference, pService);
        add((String) pReference.getProperty(mProperty), pReference);
    }

    /**
     * {@inheritDoc}
     * 
     * @see nemos.util.osgi.IServiceMap#remove(org.osgi.framework.ServiceReference)
     */
    @Override
    public void remove(final ServiceReference<S> pReference) {

        final String lKey = (String) pReference.getProperty(mProperty);

        // It was a referenced service
        if (pReference.equals(get(lKey))) {

            boolean lFound = false;
            final SortedMap<ServiceReference<S>, S> lMap = mServices.tailMap(pReference);
            for (final ServiceReference<S> lRef : lMap.keySet()) {

                // The first replacement is the best (sorted map)
                if (!lRef.equals(pReference) && lKey.equals(lRef.getProperty(mProperty))) {
                    super.put(lKey, lRef);
                    lFound = true;
                    break;
                }
            }

            // No replacement found
            if (!lFound) {
                super.remove(lKey);
            }
        }

        // Remove from services map
        if (mServices.remove(pReference) != null) {
            if (mContext != null) {
                mContext.ungetService(pReference);
            }
        }
    }
}
